Skip navigation
NintendoAge
Welcome, Guest! Please Login or Join
Loading...

## Advanced Nerdy Nights #2 MMC1 CHR and PRG Bank switching, WRAM + Battery

- Posts 1 - 9 of 9
- 1

May 15, 2010 at 2:53:02 AM
bunnyboy (81)
(Funktastic B) < Bowser >
Posts: 7328 - Joined: 02/28/2007
California
Profile
This Week: The MMC1 is the first advanced mapper made by Nintendo. It is used for many games including top titles like The Legend of Zelda. The main benefits are mirroring control, up to 256KB of PRG ROM, 128KB of CHR RAM or ROM, and 8KB of WRAM. The WRAM can be battery backed for saved games. This tutorial will cover all features of the MMC1 and how to use them. You should be comfortable with the normal Nerdy Nights series before starting. Another more simple lesson for bankswitching is Advanced Nerdy Nights #1. If you only need one or two of the banking features then you may want to consider more simple and cheaper mappers instead such as UNROM or CNROM.
Carts using the MMC1 will have the S*ROM board code, like SNROM and SGROM. BootGod's NesCartDB database can be searched for which games use which boards. The ReproPak MMC1 board can also be used to build carts.
Shift Registers
The MMC1 uses a 5 bit shift register to temporarily store the banking bits. Shift registers were covered in Week 7. When writing to the register data comes in from data bit 0 only. This is similar to the controller reading where data outputs to data bit 0. Every time a write happens the current bits are shifted and D0 is inserted. The first bit you write eventually becomes to lowest bank bit. On the 5th write when the shift register is full the 5 bit value gets copied to the banking register. At this point the bank switch happens immediately without any delays. To load a bank register the LSR instruction is used for shifting:

      LDA banknumber
      STA bankreg    ; load bank bit 0 to shift register from data bit 0
      LSR A          ; shift in next data bit to position 0
      STA bankreg    ; load bank bit 1 from data bit 0
      LSR A
      STA bankreg    ; bank bit 2
      LSR A
      STA bankreg    ; bank bit 3
      LSR A
      STA bankreg    ; bank bit 4, bank register loaded, bank switch happens here

Unlike other simple mappers like UNROM and CNROM, there are no bus conflicts. The ROM is not enabled while you are writing so you do not have to make the data you are writing match.
Data bit 7 is also connected to the MMC1. When a write happens to any banking register with D7=1 the shift register is reset back to position 0. It will then take another 5 writes to fully load the next value. All other bits are ignored and D0 is not loaded into the shift register. The PRG bits of the control register are also reset to their default values as shown in the next section. Usually you will only reset the MMC1 at the very beginning of your program:

      LDA #%10000000
      STA $8000

Config Register at $8000-9FFF
To load the config register, do 5 writes to the $8000-9FFF range. The config bits are:

      43210
      -----
      CPRMM
      |||||
      |||++- Mirroring (0: one-screen, lower bank; 1: one-screen, upper bank;
      |||               2: vertical; 3: horizontal)
      ||+--- PRG swap range (0: switch 16 KB bank at $C000; 1: switch 16 KB bank at $8000;
      ||                            only used when PRG bank mode bit below is set to 1)
      |+---- PRG size (0: switch 32 KB at $8000, ignoring low bit of bank number;
      |                         1: switch 16 KB at address specified by location bit above)
      +----- CHR size (0: switch 8 KB at a time; 1: switch two separate 4 KB banks)

Mirroring Config
Your program can change the mirroring at any point using these bits. You do not need to wait for vblank to change them. When using the MMC1 the .inesmir directive bit is ignored. You must set it through your code. Mirroring set to 0 and 1 are single screen mirroring modes. Only 1KB is used for all nametables. When scrolling the screen will wrap both vertically and horizontally. Mirroring set to 2 is the typical vertical mirroring, and 3 is horizontal mirroring.
PRG Bank Size Config
The MMC1 swaps PRG ROM in either 16KB or 32KB chunks. By default this bit is set to 1 for 16KB banks. Clearing it to 0 enables 32KB banks. Notice these are not the same size as the 8KB NESASM banks so the bank numbers will be different. When using 16KB banks the MMC1 banks are twice as big, so you must divide your NESASM bank number by 2 when writing it to the bank register. When using 32KB banks you must divide by 4. 16KB banks is most commonly used, with the bulk of the code in the fixed bank and data/graphics/music in the swappable banks.
PRG Swap Range Config
When using 16KB banks set above, the PRG address range that gets swapped can be configured. If 32KB banks are used this bit is ignored and the entire $8000-FFFF range is swapped at once.
By default this bit is set to 1, making the $8000-BFFF range swappable while the $C000-FFFF range is fixed to the last bank Of PRG. This matches the PRG swapping of the UNROM mapper and is most commonly used. Clearing this bit to 0 changes this so $8000-BFFF is fixed and $C000-FFFF is swappable.
Changing the range or bank size can be useful for swapping audio samples but you have to be careful to put IRQ/reset/NMI vectors in all banks that are loaded into the vector area at $FFFA-FFFF.
CHR Bank Size Config
Like the PRG the CHR bank size can be configured to either 4KB or 8KB banks. With 8KB banks the whole $0000-1FFF range is one bank. With 4KB banks there are two banks at PPU $0000-0FFF and $1000-1FFF. This can be used with background in one bank and sprites in another. Then, for example, all sprites could be swapped and the background could stay.
CHR Bank 0 Register at $A000-BFFF
This is the register for CHR bank 0. To set it do 5 writes to the $A000-BFFF range. When in 4KB CHR mode it selects a bank for PPU $0000-0FFF. The full 5 bit value is used so there are 32 possible banks. Each bank is 4KB making it 128KB CHR maximum. When in 8KB CHR mode this register controls the full PPU $0000-1FFF. The bottom bit is ignored so there are 16 possible banks. Each bank is now 8KB which is still 128KB max.

4KB mode | 8KB mode
controls $0000-0FFF | controls $0000-1FFF bottom bit is ignored

CHR Bank 1 Register at $C000-DFFF
This is the register for CHR bank 1. To set it do 5 writes to the $C000-DFFF range. When in 4KB CHR mode it selects a bank for PPU $1000-1FFF. When in 8KB CHR mode it is completely ignored.

4KB mode | 8KB mode
controls $1000-1FFF | register ignored

PRG Bank Register at $E000-FFFF
This is the register for PRG banking. To set it do 5 writes to the $E000-FFFF range. The bits are:

      43210
      -----
      WPPPP
      |||||
      |++++- Select a PRG ROM bank (low bit ignored in 32 KB mode)
      +----- WRAM chip enable (0: enabled; 1: disabled)

In 16KB PRG mode it selects a 16KB PRG bank for the current swappable address range. Only the 4 lower bits are used for 16 possible PRG banks. That is 256KB maximum. In 32KB PRG mode it selects a 32KB bank for the $8000-FFFF range. Only bits 3-1 are used for 8 possible banks. Bit 0 is ignored.

16KB mode swap=0 | 16KB mode swap=1 | 32KB mode
controls $C000-FFFF | controls $8000-BFFF (default setting) | controls $8000-FFFF low bit ignored

WRAM
Bit 5 of the PRG Bank register also controls WRAM access. Clear this bit to enable WRAM access. Setting the bit to 1 disables the WRAM. If the WRAM is used for saved games it is usually disabled when it is not being accessed to prevent unwanted writes from corrupting the saves when the console is reset.
To use WRAM in your program nothing needs to be changed in the iNES header. The emulator will assume there is WRAM based on the mapper number. Next you need to enable the WRAM in the PRG Bank register. With that bit at 0 you can now use the WRAM. It is just plain RAM that you can read or write at the $6000-7FFF range. Like the console RAM the contents are unknown when the console is powered on. All RAM is cleared to unknown values when power is removed. However the RAM is not cleared when just the reset button is pushed. Power is still going to the cart so the RAM is still valid. This can be useful for telling if the console was just turned on or was only reset.
To add a battery to the WRAM, set the .inesmir directive to 2 in the iNES header. Now read and write to the WRAM normally. When power is removed the RAM contents will remain. The emulator will create an 8KB .sav file for the WRAM, however some emulators will not do this unless you have done some WRAM access.
Banking Routines
Keeping track of all the register addresses and bits can get confusing, so a few simple routines are used instead. In general subroutines should be used for even simple bank switching so the mapper can be changed more easily later. Only the most commonly used Config and PRG Bank register routines are shown here, it is your job to write the others.

    ConfigWrite:        ; make sure this is in a fixed PRG bank so the RTS doesn't get swapped away
      LDA #$80
      STA $8000         ; reset the shift register
      LDA #%00001110    ; 8KB CHR, 16KB PRG, $8000-BFFF swappable, vertical mirroring
      STA $8000         ; first data bit
      LSR A             ; shift to next bit
      STA $8000         ; second data bit
      LSR A             ; etc
      STA $8000
      LSR A
      STA $8000
      LSR A
      STA $8000         ; config bits written here, takes effect immediately
      RTS

    PRGBankWrite:       ; make sure this is in a fixed bank so it doesnt get swapped away
      LDA bankNumber    ; get bank number into A
      STA $E000         ; first data bit
      LSR A             ; shift to next bit
      STA $E000
      LSR A
      STA $E000
      LSR A
      STA $E000
      LSR A
      STA $E000         ; bank switch happens immediately here
      RTS

Putting It All Together
Download and unzip the cyoammc1.zip sample files. The CYOA code has been changed to the MMC1 mapper. Running the cyoammc1.bat file will create cyoammc1.nes, which will run in an emulator. This sample uses 8KB of CHR RAM so no CHR banking has been included. The changes from UNROM PRG mapping are minimal. Some of the variables have been moved to the WRAM area to show how to use it. An example of using WRAM to detect reset is also included in resetCount. Try changing the battery info in the iNES header to see how it makes resetCount change between power and resets.
Edited: 05/15/2010 at 02:56 AM by bunnyboy
May 15, 2010 at 3:08:28 AM
NESHomebrew (17)
(Brad Bateman - Strange Brew Games) < King Solomon >
Posts: 4160 - Joined: 04/28/2008
Saskatchewan
Profile
Our Nerdy Nights Master Returns!!! Just wondering, is the scrolling tutorial mentioned in previous Nerdy Nights still in the works? *edit* maybe it wasn't mentioned as I can't seem to find it, but there have been a few requests.
Edited: 05/15/2010 at 03:16 AM by NESHomebrew
May 16, 2010 at 12:03:02 PM
Mario's Right Nut (350)
(Cunt Punch) < Bowser >
Posts: 6574 - Joined: 11/21/2008
Texas
Profile
This is pretty cool. Thanks for the info. For some reason, on my computer here, it looks like all of your paragraphs are in one line of text and you have to scroll way right off the screen to read the whole thing. Is that a setting I have messed up?
Any chance of expanding this to include SUROM? I've made it work, but it is not pretty.
-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

Check out my dev blog.

May 16, 2010 at 1:45:46 PM
bunnyboy (81)
(Funktastic B) < Bowser >
Posts: 7328 - Joined: 02/28/2007
California
Profile
Originally posted by: Mario's Right Nut
This is pretty cool. Thanks for the info. For some reason, on my computer here, it looks like all of your paragraphs are in one line of text and you have to scroll way right off the screen to read the whole thing. Is that a setting I have messed up?
Any chance of expanding this to include SUROM? I've made it work, but it is not pretty.
Not sure about the formatting, blame Dain! Maybe it needs more P or DIV tags...
PRG/WRAM Expansions
Ok, when I gave the max sizes above I lied! There are three boards that offer various expansion sizes. All of them use CHR RAM then steal CHR address bits to be used for PRG banks.
SUROM
This board is used by DW3 and DW4. It uses 8KB of CHR RAM and the games set the CHR bank size to 8KB. That means neither CHR bank register is used for CHR, because there is only one bank. Instead the top bit of the CHR bank register (D4) becomes an additional top bit of the PRG Bank. That gives you 5 bank bits for 32 banks of 16KB, or 512KB of PRG ROM. Clearing the CHR bank D4 accesses the first 16 banks, and setting it accesses the second 16 banks.
In 8KB CHR mode the CHR bank 1 register is still ignored, so you only have to set CHR bank 0. However in 4KB CHR mode you have to set D4 in both registers the same or else the PRG will be swapping while the screen is being rendered without you controlling it. When the PPU switches between background and sprites your PRG would also switch.
Typically you will want to use 16KB PRG mode and keep all your bank switching code in the last bank. Doing the PRG switch happens in 2 steps (CHR bank register and PRG bank register) and you don't want the first step to switch away your code. The other method is to copy your bank switching code to RAM and execute it there.
SOROM
This board is used by Genghis Khan, Nobunaga, and a few other games. The same idea of stealing unused CHR bits is used to expand the WRAM to 16KB. This time the fourth bit of the CHR Bank register (D3) becomes the WRAM bank select. Clear the bit to select the lower 8KB of WRAM for the $6000-7FFF range, and set the bit to select the upper 8KB. The board uses two physically separate chips and only one is battery backed, so only one of the 8KB banks is battery backed.
Like with SUROM when using 4KB mode you need to set D3 the same in both CHR registers.
SXROM
This board is only used in a couple Japanese games like Final Fantasy 1+2. It is a combination of SUROM and SOROM to expand both PRG ROM and WRAM. Makes sense when they pack two games on one cart that each need half those specs! Unlike SOROM there is only one physical WRAM chip so all 16KB is battery backed.
D4 still picks upper or lower 256KB PRG, and D3 still picks upper or lower 8KB WRAM.
If someone wanted to wire up a custom board, D2 and D1 could be used in the same way for even more PRG.
May 17, 2010 at 7:45:09 PM
Mario's Right Nut (350)
(Cunt Punch) < Bowser >
Posts: 6574 - Joined: 11/21/2008
Texas
Profile
Originally posted by: bunnyboy
SUROM
This board is used by DW3 and DW4. It uses 8KB of CHR RAM and the games set the CHR bank size to 8KB. That means neither CHR bank register is used for CHR, because there is only one bank. Instead the top bit of the CHR bank register (D4) becomes an additional top bit of the PRG Bank. That gives you 5 bank bits for 32 banks of 16KB, or 512KB of PRG ROM. Clearing the CHR bank D4 accesses the first 16 banks, and setting it accesses the second 16 banks.
In 8KB CHR mode the CHR bank 1 register is still ignored, so you only have to set CHR bank 0. However in 4KB CHR mode you have to set D4 in both registers the same or else the PRG will be swapping while the screen is being rendered without you controlling it. When the PPU switches between background and sprites your PRG would also switch.
Typically you will want to use 16KB PRG mode and keep all your bank switching code in the last bank. Doing the PRG switch happens in 2 steps (CHR bank register and PRG bank register) and you don't want the first step to switch away your code. The other method is to copy your bank switching code to RAM and execute it there.

So, here you're saying that your "dominate" bank would have to be exactly the same in each 16 bank set if you were to do it the first way you mentioned? This would keep your addresses all in line. Correct?

The second method sounds somewhat more efficent, right? I appoligize for being thick here, but I really can't wrap my head around how you would do that. And here wouldn't you have to have reset code in the second set of banks to at least switch to the first one as it could load any random bank during start up? Don't suppose you have a quick example of this? At least the bank switching via RAM part?

Thank you for the help.

-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

Check out my dev blog.

Jul 1, 2014 at 7:09:58 PM
MODERATOR
Vectrex280996 (115)
(King Ghidorah) < Bowser >
Posts: 6251 - Joined: 07/28/2012
Switzerland
Profile
Seems like "advanced" bankswitching is not that hard to pull off. This might be interesting for the Mystique multicart I'd like to make at some point!
However, MMC1 stuff is still kinda new to me, and before trying to program my own MMC1 program, I'd like you to clarify a few things for me.
What I understand about the shift register use is that only the first bit is used (Kind of like the controller). You have to do an LSR to get the previous bits to this particular bit
(Ex: 0001001[1] C0 - Only the byte in the brackets is read, then as you LSR, the bytes you wanna read go to the right, like this - 0000100[1] C1)
Also, if I wrote this to the config at $8000, it should be 1 - two 4KB CHR banks, 0 - 32 KB PRG switch at once, 0-Not used due to 32KB swap, 11-Horizontal Mirroring
Is that how it works?
However, I don't really understand why you have to write to the registers in $XXXX-$YYYY ranges... Aren't those ranges used for the PRG? Thanks for enlightening me on that
Edited: 07/01/2014 at 07:10 PM by Vectrex280996
Jul 2, 2014 at 8:59:56 PM
thefox (0)
(Kalle Immonen) < Meka Chicken >
Posts: 519 - Joined: 07/08/2008
Finland
Profile
Originally posted by: Vectrex280996
What I understand about the shift register use is that only the first bit is used (Kind of like the controller). You have to do an LSR to get the previous bits to this particular bit
(Ex: 0001001[1] C0 - Only the byte in the brackets is read, then as you LSR, the bytes you wanna read go to the right, like this - 0000100[1] C1)
Also, if I wrote this to the config at $8000, it should be 1 - two 4KB CHR banks, 0 - 32 KB PRG switch at once, 0-Not used due to 32KB swap, 11-Horizontal Mirroring
Is that how it works?
That's correct.
Originally posted by: Vectrex280996
However, I don't really understand why you have to write to the registers in $XXXX-$YYYY ranges... Aren't those ranges used for the PRG? Thanks for enlightening me on that
The $8000-FFFF range is used for PRG yeah, but since it's ROM the writes to the same area can be reused by mappers for their own purposes. So "why" is not really the right question to ask here, it just is the way it is.
-------------------------
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi
Nov 23, 2015 at 8:50:42 PM
splitpane (0)
< Cherub >
Posts: 7 - Joined: 11/13/2015
Alabama
Profile
First of all, thanks a lot, BunnyBoy, for these tutorials. I've learned a lot and made it all the way through in a couple of weeks, and have managed to get a lot of key parts of my game working as proof of concept code already. But now I've been working through the example in this lesson and trying to figure out CHR RAM, but not having a lot of luck. I'm looking to use CHR RAM to draw custom tiles. The example program here is all about loading a lot of text (and from the .ineschr 0 at the beginning I guess doesn't even use CHR RAM) but I'm trying to figure out how to just draw some pixels. I changed the first two lines, removed the text file loading, and started removing a lot of the text printing code, but I am having trouble seeing clearly what is the core of the CHR RAM code. Let's say I want to do nothing but update a few tiles of background with some random pixels. Can anybody point me toward a minimal sample? Or suggest what the minimal steps would be?
Nov 24, 2015 at 10:16:28 AM
Mega Mario Man (39)
< Lolo Lord >
Posts: 1904 - Joined: 02/13/2014
United States
Profile
Originally posted by: splitpane
First of all, thanks a lot, BunnyBoy, for these tutorials. I've learned a lot and made it all the way through in a couple of weeks, and have managed to get a lot of key parts of my game working as proof of concept code already. But now I've been working through the example in this lesson and trying to figure out CHR RAM, but not having a lot of luck. I'm looking to use CHR RAM to draw custom tiles. The example program here is all about loading a lot of text (and from the .ineschr 0 at the beginning I guess doesn't even use CHR RAM) but I'm trying to figure out how to just draw some pixels. I changed the first two lines, removed the text file loading, and started removing a lot of the text printing code, but I am having trouble seeing clearly what is the core of the CHR RAM code. Let's say I want to do nothing but update a few tiles of background with some random pixels. Can anybody point me toward a minimal sample? Or suggest what the minimal steps would be?
.ineschr 0 just means that you are not using CHR ROM. "This sample uses 8KB of CHR RAM so no CHR banking has been included."
Here is the CHR RAM loading subroutine in the tutorial. Really, it's not much different than what we did before, you are just reading the .chr file (or in the tutorials case, the graphics.nes file) and loading it into the CHRRAM area on the PPU. I literally just went through this tutorial Sunday night so I could prep my game to flash to my my UNROM flash board. As for answering your question on how to load the screen with random pixels, about the only think I can think of is to use a Pointer Table with the tiles you want to use and a random number generator subroutine to select tiles from the table at random and then write them to the screen. Maybe I'm wrong on this though.
GRAPHICS TILES AND SPRITES STORED IN BANK 14
.bank 14
.org $C000 ;;8KB graphics in this fixed bank
Graphics:
.incbin "graphics.nes"
.incbin "graphics.nes"
CODE TO LOAD PPU WITH CHRRAM
LoadCHRRAM: ;;copies 8KB of graphics from PRG to CHR RAM
lda $2002
lda #$00
sta $2006 ;set PPU to the CHR RAM area $0000-1FFF
sta $2006
ldy #$00
ldx #$20 ;32 x 256 bytes = 8 KB
lda #LOW(Graphics)
sta sourceLo
lda #HIGH(Graphics) ;get the address of the graphics data ($C000)
sta sourceHi ;put into our source pointer
LoadCHRRamLoop:
lda [sourceLo], y ;copy from source pointer
sta $2007 ;to PPU CHR RAM area
iny
bne LoadCHRRamLoop ;;loop 256 times
inc sourceHi ;;then increment the high address byte
dex ;;do that 32 times
bne LoadCHRRamLoop ;;32 x 256 = 8KB
LoadCHRRamDone:
rts
-------------------------
Current Project
Tailgate Party
For Sale - http://nintendoage.com/forum/mess...
WTB - http://nintendoage.com/forum/mess...
My Homebrews - Orab Games - On Facebook
Happy Hour , Power Pad Demo

Login
User Name:
Password
Stay logged in
Forget your login? Join NintendoAge!